1 module hip.windowing.platforms.x11;
2 
3 version(Android){}
4 else version(linux) version = X11;
5 version(X11):
6 
7 import core.stdc.stdio;
8 
9 import hip.windowing.platforms.x11lib.glx;
10 import hip.windowing.platforms.x11lib.x11;
11 import hip.windowing.events;
12 import hip.windowing.input;
13 
14 public import hip.windowing.platforms.x11lib.x11;
15 
16 package struct X11WindowData
17 {
18     Display* display;
19     Window window;
20     Screen* screen;
21     XVisualInfo* visual;
22     int screenId;
23     int width, height;
24     Colormap colormap;
25     GLXContext glContext;
26     Atom atomWmDeleteWindow;
27 
28 }
29 package X11WindowData x11win;
30 
31 package __gshared bool ctxErrorOccurred = false;
32 
33 package extern(C) nothrow @system @nogc
34 int ctxErrorHandler( Display *dpy, XErrorEvent *ev )
35 {
36     ctxErrorOccurred = true;
37     return 0;
38 }
39 
40 nothrow @nogc bool initializeOpenGL(int majorVersion, int minorVersion)
41 {
42     GLint[23] glxAttribs = [
43         GLX_X_RENDERABLE    , True,
44 		GLX_DRAWABLE_TYPE   , GLX_WINDOW_BIT,
45 		GLX_RENDER_TYPE     , GLX_RGBA_BIT,
46 		GLX_X_VISUAL_TYPE   , GLX_TRUE_COLOR,
47 		GLX_RED_SIZE        , 8,
48 		GLX_GREEN_SIZE      , 8,
49 		GLX_BLUE_SIZE       , 8,
50 		GLX_ALPHA_SIZE      , 8,
51 		GLX_DEPTH_SIZE      , 24,
52 		GLX_STENCIL_SIZE    , 8,
53 		GLX_DOUBLEBUFFER    , True,
54 		None
55     ];
56     
57     int fbcount;
58     GLXFBConfig* fbc = glXChooseFBConfig(x11win.display, x11win.screenId, glxAttribs.ptr, &fbcount);
59     if (fbc is null || fbcount == 0) 
60     {
61         printf("Failed to retrieve framebuffer.\n");
62         XCloseDisplay(x11win.display);
63         return 1;
64     }
65 
66     // Pick the FB config/visual with the most samples per pixel
67 	int best_fbc = -1, worst_fbc = -1, best_num_samp = -1, worst_num_samp = 999;
68 	for (int i = 0; i < fbcount; ++i) {
69 		XVisualInfo *vi = glXGetVisualFromFBConfig( x11win.display, fbc[i] );
70 		if ( vi != null) {
71 			int samp_buf, samples;
72 			glXGetFBConfigAttrib( x11win.display, fbc[i], GLX_SAMPLE_BUFFERS, &samp_buf );
73 			glXGetFBConfigAttrib( x11win.display, fbc[i], GLX_SAMPLES       , &samples  );
74 
75 			if ( best_fbc < 0 || (samp_buf && samples > best_num_samp) ) {
76 				best_fbc = i;
77 				best_num_samp = samples;
78 			}
79 			if ( worst_fbc < 0 || !samp_buf || samples < worst_num_samp )
80 				worst_fbc = i;
81 			worst_num_samp = samples;
82 		}
83 		XFree( vi );
84 	}
85 
86 
87     //Some would try to get the best framebuffer, but, is that really necessary? Get the first
88     GLXFBConfig bestFbc = fbc[best_fbc];
89     XFree(fbc);
90     x11win.visual = glXGetVisualFromFBConfig(x11win.display, bestFbc);
91     if(x11win.visual == null)
92     {
93         printf("Could not create correct visual window \n");
94         XCloseDisplay(x11win.display);
95         return false;
96     }
97 
98     //Open the window
99     XSetWindowAttributes windowAttribs;
100     windowAttribs.border_pixel = BlackPixel(x11win.display, x11win.screenId);
101     windowAttribs.background_pixel = WhitePixel(x11win.display, x11win.screenId);
102     windowAttribs.override_redirect = True;
103     windowAttribs.colormap = XCreateColormap(
104         x11win.display, RootWindow(x11win.display, x11win.screenId), x11win.visual.visual, AllocNone
105     );
106 
107     windowAttribs.event_mask = PointerMotionMask |
108                              ButtonPressMask |
109                              ButtonReleaseMask |
110 	    		     KeyPressMask |
111 	    		     KeyReleaseMask |
112                              EnterWindowMask |
113                              LeaveWindowMask |
114                              ExposureMask;
115 
116 
117 
118     x11win.window = XCreateWindow(
119         x11win.display, 
120         RootWindow(x11win.display, x11win.screenId), 
121         0, 0, x11win.width, x11win.height,
122         0, x11win.visual.depth, InputOutput, x11win.visual.visual,
123         CWBackPixel | CWColormap | CWBorderPixel | CWEventMask, 
124         &windowAttribs
125     );
126     if(!x11win.window)
127     {
128         printf("Could not create XWindow\n");
129         return false;
130     }
131 
132     Atom atomWmDeleteWindow = XInternAtom(x11win.display, "WM_DELETE_WINDOW", False);
133     if(atomWmDeleteWindow == BadAlloc)
134     {
135         printf("X Server failed to allocate WM_DELETE_WINDOW\n");
136         return false;
137     }
138     else if(atomWmDeleteWindow == BadValue)
139     {
140         printf("WM_DELETE_WINDOW is not a valid argument\n");
141         return false;
142     }
143     x11win.atomWmDeleteWindow = atomWmDeleteWindow;
144     Status st = XSetWMProtocols(x11win.display, x11win.window, &atomWmDeleteWindow, 1);
145     if(st == BadAlloc)
146     {
147         printf("XServer failed to allocate resources for SetWMProtocols\n");
148         return false;
149     }
150     else if(st == BadWindow)
151     {
152         printf("The window argument does not name a defined window\n");
153         return false;
154     }
155 
156     const(char)* glExts = glXQueryExtensionsString(x11win.display, DefaultScreen(x11win.display));
157 
158     glXCreateContextAttribsARBProc glXCreateContextAttribsARB;
159     glXCreateContextAttribsARB = cast(glXCreateContextAttribsARBProc)glXGetProcAddressARB(cast(const(GLubyte)*)"glXCreateContextAttribsARB");
160 
161     GLXContext glContext;
162 
163 
164     if(!isExtensionSupported(glExts, "GLX_ARB_create_context") || glXCreateContextAttribsARB is null)
165     {
166         printf("glXCreateContextAttribsARB() not found, using old style GLX context");
167         x11win.glContext = glContext = glXCreateNewContext(x11win.display, bestFbc, GLX_RGBA_TYPE, null, True);
168     }
169     else
170     {
171         int[7] context_attribs =
172         [
173             GLX_CONTEXT_MAJOR_VERSION_ARB, 3,
174             GLX_CONTEXT_MINOR_VERSION_ARB, 3, //3.3 is the minimum here
175             GLX_CONTEXT_FLAGS_ARB        , GLX_CONTEXT_CORE_PROFILE_BIT_ARB,
176             None
177         ];
178 
179         x11win.glContext = glContext = glXCreateContextAttribsARB(x11win.display, bestFbc, null,
180                                       True, context_attribs.ptr );
181     }
182     if(x11win.glContext == null)
183     {
184         printf("Could not create GLX Context\n");
185         return false;
186     }
187     XSync(x11win.display, False);
188 
189     if (!glXIsDirect (x11win.display, x11win.glContext)) 
190 		printf("Indirect GLX rendering context obtained\n");
191     if(glXMakeCurrent(x11win.display, x11win.window, x11win.glContext) == 0)
192     {
193         printf("Could not make GLX Context as current\n");
194         return false;
195     }
196     //Show Window
197     XClearWindow(x11win.display, x11win.window);
198     XMapRaised(x11win.display, x11win.window);
199     XStoreName(x11win.display, x11win.window, "HipremeEngine");
200     
201     setVsyncActive(false);
202 
203 
204     return true;
205 }
206 
207 void show(){}
208 void swapBuffer()
209 {
210     glXSwapBuffers(x11win.display, x11win.window);
211 }
212 
213 void setVsyncActive(bool active) @nogc nothrow @system
214 {
215     static bool loadedSymbols = false;
216     if(!loadedSymbols)
217     {
218         glXSwapIntervalEXT = cast(glXSwapIntervalEXTProc)glXGetProcAddressARB(cast(GLubyte*)"glXSwapIntervalEXT");
219         glXSwapIntervalMESA = cast(glXSwapIntervalMESAProc)glXGetProcAddressARB(cast(GLubyte*)"glXSwapIntervalMESA");
220         glXSwapIntervalSGI = cast(glXSwapIntervalSGIProc)glXGetProcAddressARB(cast(GLubyte*)"glXSwapIntervalSGI");
221         loadedSymbols = true;
222     }
223     glXSwapIntervalEXT(x11win.display, x11win.window, cast(int)active);
224     glXSwapIntervalMESA(cast(int)active);
225     glXSwapIntervalSGI(cast(int)active);
226 }
227 
228 void setWindowName(string name)
229 {
230     XStoreName(x11win.display, x11win.window, name.ptr);
231 }
232 
233 
234 pragma(inline) wchar convertKeycodeToScancode(XKeyEvent* ev)
235 {
236     char[2] buffer = '\0';
237     KeySym ks;
238     int allocated = XLookupString(ev, buffer.ptr, buffer.length, &ks, null);
239     if(allocated > 1)
240         printf("%*s", cast(int)buffer.length, buffer.ptr);
241     return *cast(wchar*)(cast(void*)buffer.ptr);
242 }
243 
244 void poll()
245 {
246     XEvent ev;
247     while (XPending(x11win.display) > 0) 
248     {
249         XNextEvent(x11win.display, &ev);
250         switch(ev.type)
251         {
252             case Expose:
253             {
254                 XWindowAttributes attribs;
255                 XGetWindowAttributes(x11win.display, x11win.window, &attribs);
256                 printf("Expose event\n");
257                 break;
258             }
259             case ClientMessage:
260                 if(ev.xclient.data.l[0] == x11win.atomWmDeleteWindow && onWindowClosed != null)
261                     onWindowClosed();
262                 break;
263             case DestroyNotify:
264             {
265                 if(onWindowClosed != null)
266                     onWindowClosed();
267                 break;
268             }
269             case ButtonPress:
270             {
271                 int x = ev.xbutton.x;
272                 int y = ev.xbutton.y;
273                 switch(ev.xbutton.button)
274                 {
275                     case 1: //Left
276                         if(onMouseDown != null)
277                             onMouseDown(HipWindowingMouseButton.left, x, y);
278                         break;
279                     case 2: //Middle
280                         if(onMouseDown != null)
281                             onMouseDown(HipWindowingMouseButton.middle, x, y);
282                         break;
283                     case 3: //Right
284                         if(onMouseDown != null)
285                             onMouseDown(HipWindowingMouseButton.right, x, y);
286                         break;
287                     case 4: //Scroll up
288                         if(onMouseWheel != null)
289                             onMouseWheel(0, -1);
290                         break;
291                     case 5: //Scroll down
292                         if(onMouseWheel != null)
293                             onMouseWheel(0, 1);
294                         break;
295                     default: break;
296                 }
297             } break;
298             case ButtonRelease:
299             {
300                 int x = ev.xbutton.x;
301                 int y = ev.xbutton.y;
302                 switch(ev.xbutton.button)
303                 {
304                     case 1: //Left
305                         if(onMouseUp != null)
306                             onMouseUp(HipWindowingMouseButton.left, x, y);
307                         break;
308                     case 2: //Middle
309                         if(onMouseUp != null)
310                             onMouseUp(HipWindowingMouseButton.middle, x, y);
311                         break;
312                     case 3: //Right
313                         if(onMouseUp != null)
314                             onMouseUp(HipWindowingMouseButton.right, x, y);
315                         break;
316                     default: break;
317                 }
318             } break;
319             case MotionNotify:
320                 if(onMouseMove != null)
321                     onMouseMove(ev.xmotion.x, ev.xmotion.y);
322                 break;
323             case KeyPress:
324                 if(onKeyDown != null)
325                     onKeyDown(cast(uint)XKeycodeToKeysym(x11win.display, ev.xkey.keycode, ev.xkey.state & ShiftMask ? 1 : 0));
326                     // onKeyDown(convertKeycodeToScancode(&ev.xkey));
327                 break;
328             case KeyRelease:
329                 if(onKeyUp != null)
330                     onKeyUp(cast(uint)XKeycodeToKeysym(x11win.display, ev.xkey.keycode, ev.xkey.state & ShiftMask ? 1 : 0));
331                     // onKeyUp(convertKeycodeToScancode(&ev.xkey));
332                 break;
333             default:break;
334         }
335     }
336 }
337 
338 ///Returns [width, height]
339 int[2] getWindowSize()
340 {
341     XWindowAttributes att;
342     XGetWindowAttributes(x11win.display, x11win.window, &att);
343     return [att.width, att.height];
344 }
345 
346 void setWindowSize(int width, int height)
347 {
348     uint change_values = CWWidth | CWHeight;
349     XWindowChanges values;
350     values.width = width;
351     values.height = height;
352     XConfigureWindow(x11win.display, x11win.window, change_values, &values);
353 }
354 
355 bool destroy_GL_Context()
356 {
357     XDestroyWindow(x11win.display, x11win.window);
358     XCloseDisplay(x11win.display);
359     XFree(x11win.visual);
360     XFreeColormap(x11win.display, x11win.colormap);
361     glXDestroyContext(x11win.display, x11win.glContext);
362     return true;
363 }
364 
365 int openWindow(int width, int height)
366 {
367     x11win.width = width;
368     x11win.height = height;
369     //Open the display
370     x11win.display = XOpenDisplay(null);
371     if(x11win.display == null)
372     {
373         printf("Could not open display.\n");
374         return 1;
375     }
376     int glx_major, glx_minor;
377     if ( !glXQueryVersion(x11win.display, &glx_major, &glx_minor ) || 
378        ( ( glx_major == 1 ) && ( glx_minor < 3 ) ) || ( glx_major < 1 ) )
379     {
380         printf("Invalid GLX version\n");
381         return 1;
382     }
383     x11win.screenId = DefaultScreen(x11win.display);
384     
385     return 0;
386 }